﻿using System;
using System.Windows;
using System.Windows.Threading;
using GammaJul.LgLcd;
using GammaJul.LgLcd.Wpf;
using VosSoft.ZuneLcd.Api;

namespace VosSoft.ZuneLcd.Color
{
    /// <summary>
    /// Application class.
    /// </summary>
    internal class App : Application
    {
        private LcdApplet applet;
        private LcdDeviceQvga device;

        private enum Page { Main, TrackList }
        private Page currentPage = Page.Main;

        private MainPage mainPage;
        private ZuneLcdApi zune;

        private DispatcherTimer mainTimer;

        private delegate void Action();

        private void Invoke(Action action)
        {
            if (CheckAccess())
                action();
            else
                Dispatcher.BeginInvoke(action, DispatcherPriority.Render);
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            applet = new LcdApplet("Zune LCD", LcdAppletCapabilities.Qvga, false);

            applet.DeviceArrival += new EventHandler<LcdDeviceTypeEventArgs>(applet_DeviceArrival);
            applet.DeviceRemoval += new EventHandler<LcdDeviceTypeEventArgs>(applet_DeviceRemoval);
            applet.IsEnabledChanged += new EventHandler(applet_IsEnabledChanged);
            applet.ConnectionDisrupted += new EventHandler(applet_ConnectionDisrupted);

            applet.Connect();
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);

            if (mainTimer != null)
                mainTimer.Stop();

            if (device != null)
            {
                if (device.CurrentPage != null)
                    device.CurrentPage.Dispose();
                device.Dispose();
            }

            if (applet != null)
            {
                if (applet.IsConnected)
                    applet.Disconnect();
                applet.Dispose();
            }
        }

        private void applet_DeviceArrival(object sender, LcdDeviceTypeEventArgs e)
        {
            Invoke(OnQvgaDeviceArrived);
        }

        private void OnQvgaDeviceArrived()
        {
            if (device == null)
            {
                device = (LcdDeviceQvga)applet.OpenDeviceByType(LcdDeviceType.Qvga);

                mainPage = new MainPage();
                LcdWpfPage page = new LcdWpfPage(device)
                {
                    Element = mainPage,
                    DesiredFramerate = 60
                };
                device.Pages.Add(page);
                device.CurrentPage = page;

                device.SoftButtonsChanged += new EventHandler<LcdSoftButtonsEventArgs>(device_SoftButtonsChanged);

                zune = new ZuneLcdApi();

                zune.Ready += new EventHandler(zune_Ready);
                zune.Closed += new EventHandler(zune_Closed);

                zune.TrackStatusChanged += new EventHandler(zune_TrackStatusChanged);
                zune.TrackChanged += new EventHandler(zune_TrackChanged);
                zune.TrackPositionChanged += new EventHandler(zune_TrackPositionChanged);
                zune.TrackRatingChanged += new EventHandler(zune_TrackRatingChanged);

                zune.FastForwardChanged += new EventHandler(zune_FastForwardChanged);
                zune.VolumeChanged += new EventHandler(zune_VolumeChanged);

                zune.MuteChanged += new EventHandler(zune_MuteChanged);
                zune.RepeatChanged += new EventHandler(zune_RepeatChanged);
                zune.ShuffleChanged += new EventHandler(zune_ShuffleChanged);

                zune.Launch();

                mainTimer = new DispatcherTimer(TimeSpan.FromSeconds(1.0 / device.CurrentPage.DesiredFramerate),
                    DispatcherPriority.Render, MainTimer_Tick, Dispatcher.CurrentDispatcher);

                //VosSoft.Windows.KeyboardHookManager.KeyDown += new VosSoft.Windows.KeyHookEventHandler(KeyboardHookManager_KeyDown);
            }
            else
                device.ReOpen();

            device.DoUpdateAndDraw();
        }

        //void KeyboardHookManager_KeyDown(object sender, Windows.KeyHookEventArgs e)
        //{
        //    mainPage.txtDynamic.Text = "KeyHook => " + System.Windows.Input.KeyInterop.KeyFromVirtualKey(e.KeyCode);
        //}

        private void applet_DeviceRemoval(object sender, LcdDeviceTypeEventArgs e)
        {
            if (mainTimer != null)
                mainTimer.Stop();
        }

        private void applet_IsEnabledChanged(object sender, EventArgs e)
        {
            if (mainTimer != null)
            {
                if (applet.IsConnected && applet.IsEnabled)
                    mainTimer.Start();
                else
                    mainTimer.Stop();
            }
        }

        private void applet_ConnectionDisrupted(object sender, EventArgs e)
        {
            if (mainTimer != null)
                mainTimer.Stop();
        }

        private void device_SoftButtonsChanged(object sender, LcdSoftButtonsEventArgs e)
        {
            if (!zune.IsReady)
                return;

            switch (e.SoftButtons)
            {
                case LcdSoftButtons.Left: // go to the next page
                case LcdSoftButtons.Right:
                    if (currentPage == Page.Main)
                    {
                        currentPage = Page.TrackList;
                        zune.TrackListChanged += zune_TrackListChanged;

                        if (zune.CurrentTrack.Index >= 0)
                            GetTracks();
                    }
                    else
                    {
                        currentPage = Page.Main;
                        zune.TrackListChanged -= zune_TrackListChanged;
                    }

                    Invoke(new Action(delegate()
                    {
                        mainPage.GoToNextPage(currentPage == Page.Main, (e.SoftButtons & LcdSoftButtons.Left) == LcdSoftButtons.Left);
                    }));
                    break;
                case LcdSoftButtons.Up:
                    if (currentPage == Page.Main)
                        zune.ToggleRepeat();
                    else
                        Invoke(new Action(mainPage.SelectPreviousTrack));
                    break;
                case LcdSoftButtons.Down:
                    if (currentPage == Page.Main)
                        zune.ToggleShuffle();
                    else
                        Invoke(new Action(mainPage.SelectNextTrack));
                    break;
                case LcdSoftButtons.Ok:
                    if (currentPage == Page.Main)
                        zune.CurrentTrack.ToggleRating();
                    else // play the selected track
                    {
                        Invoke(new Action(delegate()
                        {
                            int selectedTrackIndex = ((TrackListBoxItem)mainPage.lstTracks.SelectedItem).Track.Index;
                            zune.PlayAt(selectedTrackIndex);
                        }));
                    }
                    break;
                case LcdSoftButtons.Cancel:
                    zune.ToggleMute();
                    break;
                //case LcdSoftButtons.Menu:
                //    Invoke(new Action(delegate()
                //    {
                //        mainPage.txtDynamic.Text = zune.Search("test");
                //    }));
                //    break;
            }
        }

        private void zune_Ready(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                int volume = (int)zune.Volume;
                mainPage.prgVolume.ProgressValue = volume;
                mainPage.txtVolume.Text = volume.ToString("00");

                mainPage.SetMute(zune.Muted);
                mainPage.SetRepeat(zune.Repeating);
                mainPage.SetShuffle(zune.Shuffling);
            }));
        }

        private void zune_Closed(object sender, EventArgs e)
        {
            Invoke(Shutdown);
        }

        private void zune_TrackStatusChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetTrackStateIcon(zune.TrackState, zune.FastForwarding);
            }));
        }

        private void zune_TrackChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.ChangeTrack(zune.CurrentTrack, currentPage == Page.Main);
            }));

            if (currentPage == Page.TrackList)
                GetTracks();
        }

        private void zune_TrackPositionChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.txtPosition.Text = zune.CurrentTrack.PositionFormat;

                double duration = zune.CurrentTrack.Duration.TotalSeconds;
                if (duration > 0)
                    mainPage.prgPosition.ProgressValue = zune.CurrentTrack.Position.TotalSeconds * 100 / duration;
                else
                    mainPage.prgPosition.ProgressValue = 0;
            }));
        }

        private void zune_TrackRatingChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetRating(zune.CurrentTrack.Rating);
            }));
        }

        private void zune_TrackListChanged(object sender, EventArgs e)
        {
            if (zune.TrackCount == 0) // track list removed
                Invoke(new Action(mainPage.lstTracks.Items.Clear));
            else
                GetTracks();
        }

        private void GetTracks()
        {
            // TODO: optimize track range algorithm
            const int max = 5;
            int start = zune.CurrentTrack.Index - max / 2;
            if (start < 0) start = 0; // start
            int count = max;
            if (start + count > zune.TrackCount) // end
            {
                start = zune.TrackCount - max;
                if (start < 0) start = 0; // start
                if (start + count > zune.TrackCount) // end
                    count = zune.TrackCount;
            }

            zune.GetTracks(start, count, new TrackListCallback(TracksReceived));
        }

        private void TracksReceived(Track[] tracks)
        {
            Dispatcher.BeginInvoke(new Action(delegate()
            {
                mainPage.ChangeTracks(tracks, zune.CurrentTrack.Index);
            }), DispatcherPriority.Render);
        }

        private void zune_FastForwardChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetTrackStateIcon(zune.TrackState, zune.FastForwarding);
            }));
        }

        private void zune_VolumeChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                int volume = (int)zune.Volume;
                mainPage.prgVolume.ProgressValue = volume;
                mainPage.txtVolume.Text = volume.ToString("00");
            }));
        }

        private void zune_MuteChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetMute(zune.Muted);
            }));
        }

        private void zune_RepeatChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetRepeat(zune.Repeating);
            }));
        }

        private void zune_ShuffleChanged(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                mainPage.SetShuffle(zune.Shuffling);
            }));
        }

        private void MainTimer_Tick(object sender, EventArgs e)
        {
            if (applet.IsEnabled && device != null && !device.IsDisposed)
                device.DoUpdateAndDraw();
        }

        /// <summary>
        /// Application Entry Point.
        /// </summary>
        [STAThread]
        internal static void Main()
        {
            App app = new App();
            app.Run();
        }
    }
}
